package org.jpropelleralt.utils.impl;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.reaction.Reaction;

/**
 * Assist with the tracking of a set of contents (for example in a collection), 
 * by adding/removing a listener as contents are added and removed, etc.
 */
public class ContentsTracking {
	
	private final Counter<Object> counter = new Counter<Object>();
	private final Reaction reaction;
	
	/**
	 * Make contents tracking for a given {@link Reaction}
	 * @param reaction 		The {@link Reaction} that will be added to 
	 * 						any tracked contents
	 */
	public ContentsTracking(Reaction reaction) {
		this.reaction = reaction;
	}
	
	/**
	 * Start tracking an element - add it to {@link Counter},
	 * add {@link Reaction} if we haven't already, etc.
	 * @param e		The element to track
	 */
	public void startTrackingElement(Object e) {
		
		//Ignore nulls
		if (e == null) return;
		
		//Add reference for the new element
		int newCount = counter.add(e);
		
		//If the element has just been added, we need to add reaction
		if (newCount == 1) {
			//We can only listen to a Box
			if (e instanceof Box) {
				((Box)e).features().addReaction(reaction);
			}
		}
	}
	
	/**
	 * Stop tracking an element - remove it from {@link Counter},
	 * stop listening to it if its count drops to zero,
	 * etc.
	 * @param e		The element to stop tracking
	 */
	public void stopTrackingElement(Object e) {
		
		//Ignore nulls
		if (e == null) return;
		
		//remove reference for the new element
		int newCount = counter.remove(e);
		
		//If the last reference to the element has just been removed,
		//remove reaction from it
		if (newCount == 0) {
			//Can only remove from a Box
			if (e instanceof Box) {
				((Box)e).features().removeReaction(reaction);
			}
		}
	}
	
	/**
	 * Remove {@link Reaction} from every currently counted element, 
	 * and clear {@link Counter}.
	 */
	public void clearAllTracking() {
		
		//This method bypasses removeReference(e) and just
		//stops listening to any referenced element, then clears
		//counts
		
		//Stop listening to all elements that have a reference count
		for (Object e : counter) {
			//If the element is a Box, we need to remove reaction from it
			if (e instanceof Box) {
				((Box)e).features().removeReaction(reaction);
			}
		}
		
		//Clear counter
		counter.clear();
	}

}
